package com.api.request.impl;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import com.api.client.model.ApiMetadataHandler;
import com.api.client.model.ApiRequest;
import com.api.client.model.ApiRequestFactory;
import com.api.client.model.ApiResponse;
import com.api.client.model.ApiResultHandler;
import com.api.client.model.ApiServiceMetadata;
import com.api.request.model.ApiRequestBuilder;
import com.api.request.model.RequestParameters;

public class ApiRequestBuilderImpl implements ApiRequestBuilder {

	// private static final Log logger = LogFactory.getLog(ApiScribeRequestBuilder.class);

	private final ApiRequestFactory requestFactory;
	private final ApiResultHandler resultHandler;

	private final Method serviceMethod;

	private final RequestParameters requestParameters;

	ApiRequestBuilderImpl(ApiRequestFactory requestFactory, ApiMetadataHandler metadataHandler, ApiResultHandler apiResultHandler,
			Method method) {
		if (method == null) {
			throw new IllegalArgumentException("Method cannot be null");
		}
		this.requestFactory = requestFactory;
		this.resultHandler = apiResultHandler;

		this.serviceMethod = method;
		ApiServiceMetadata metadata = metadataHandler.getServiceMetadata(method);
		this.requestParameters = new RequestParameters(metadata);
	}

	@Override
	public Method getServiceMethod() {
		return serviceMethod;
	}

	@Override
	public void setParam(String name, Object value) {
		if (name == null || value == null) {
			throw new IllegalArgumentException("Name and value cannot be null");
		}
		ApiServiceMetadata metadata = requestParameters.getMetadata();
		List<String> paths = metadata.getPathParams();
		Map<String, Class<?>> queries = metadata.getQueryParams();
		Map<String, Class<?>> forms = metadata.getFormParams();
		if (paths.contains(name)) {
			requestParameters.addPathParam(name, value);
		} else if (queries.containsKey(name)) {
			requestParameters.addQueryParam(name, value);
		} else if (forms.containsKey(name)) {
			requestParameters.addFormParam(name, value);
		} else {
			throw new IllegalStateException(String.format("Parameter '%s' not found in service %s", name, serviceMethod));
		}
	}

	@Override
	public void setHeader(String name, String value) {
		requestParameters.addHeaderParam(name, value);
	}

	@Override
	public void sign(String accessKey, String accessSecret) {
		if (accessKey == null) {
			throw new IllegalArgumentException("AccessKey cannot be null");
		}
		requestParameters.setAccessKey(accessKey);
		requestParameters.setAccessSecret(accessSecret);
	}

	@Override
	public ApiRequest getRequest() {
		return requestFactory.getRequest(requestParameters);
	}

	@Override
	public <T> T send() throws Exception {
		ApiResponse response = getRequest().send();

		ApiServiceMetadata metadata = requestParameters.getMetadata();

		String produces = metadata.getProduces();
		Type returnType = metadata.getResultType();

		@SuppressWarnings("unchecked")
		T result = (T) resultHandler.getResponseResult(response, produces, returnType);
		return result;
	}
}
